package cate.game.role.farm.herotrain;

import cate.common.table.d.*;
import cate.common.table.item.StarUpItemRow;
import cate.common.table.item.hero.row.HeroBaseRow;
import cate.common.table.item.staruptask.HeroStarTaskRow;
import cate.common.util.GameResult;
import cate.game.client.msg.MergeMsgSender;
import cate.game.event.hero.HeroPowerChangeEvent;
import cate.game.event.hero.HeroStarChangeEvent;
import cate.game.play.part.FightSide;
import cate.game.play.proce.play.PlayPO;
import cate.game.res.*;
import cate.game.role.Role;
import cate.game.role.bag.hero.Hero;
import cate.game.role.bag.vo.StarUpResVO;
import cate.game.role.farm.AbstractFarm;
import cate.game.role.fight.FuncPosition;
import cate.game.train.hero.msg.HeroBaseUpdateResp;
import cate.game.train.hero.msg.HeroBatchStarUpResp;
import easy.java.dev.note.NoteClass;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.function.Predicate;

import static cate.game.train.hero.TrainHeroHandler.starUpOpt;

@NoteClass(value = "英雄升星挑战战斗")
public class TrainHeroFight extends AbstractFarm {
  public TrainHeroFight() {
  }

  @Override
  public int getFuncId() {
    return GDFunc.HERO_TRAIN_CHALLENGE;
  }

  @Override
  public void onAfterOpen() {

  }

  @Override
  public int getFightFuncId() {
    return GDFunc.HERO_TRAIN_CHALLENGE;
  }


  /**
   * 英雄升星完成任务
   *
   * @param role
   * @param hero
   * @param rewardShow
   * @param taskId
   * @return
   */
  public GameResult<Hero> taskStarUp(Role role, Hero hero, Map<Integer, CostSelect> csbbMap, RewardShow rewardShow, int taskId) {
    GameResult<Hero> r = new GameResult<>();
    Hero oldHero = hero.copy();

    HeroBaseRow tpl = role.getGame().table.hero.base.get(hero.tid);
    if (tpl.nextStarId <= 0) {
      return r.fail("英雄已升到顶级");
    }

    if (!tpl.taskMap.containsValue(taskId)) {
      return r.fail("数据错误");
    }

    if (tpl.upType != GDHero.UpType.NORMAL && hero.starTaskList.stream().anyMatch(index -> index == taskId)) {
      return r.fail("重复突破");
    }

    HeroStarTaskRow taskRow = role.getGame().table.starUpTask.task.get(taskId);
    if (taskRow.preTaskId != GD.NEGATIVE && !hero.starTaskList.contains(taskRow.preTaskId)) {
      return r.fail("请先突破前一境界");
    }

    MergeMsgSender sender = new MergeMsgSender();
    if (StringUtils.isBlank(taskRow.monsterStr)) {
      r = consumeMethod(tpl, sender, taskRow, csbbMap, rewardShow);
      if (r.ok()) {
        r = successStarUpTask(tpl, hero, taskId, rewardShow);
      } else {
        return r;
      }
    } else {
      if (isFighting()) {
        return r.fail("正在战斗中");
      }
      //怪物关卡战斗
      EcResult<FightSide> fsResult = role.getGame().fight.side.createFightSideByCode(GDFight.Team.B, taskRow.monsterStr);
      if (!fsResult.ok()) {
        return r.fail(fsResult.message);
      }
      FuncPosition position = role.getFight().getFuncPositions(getFightFuncId());
      if (!position.containHero(hero.uid)) {
        return r.fail("战斗阵容必须包含当前英雄");
      }
      HeroStarUpFTC context = new HeroStarUpFTC(role, taskRow, tpl, hero, taskId);
      context.funcId = getFightFuncId();
      context.setPositionApply(getFightFuncId());
      context.playStory.params.put("uid", hero.uid);
      context.playStory.params.put("taskId", taskId);

      EcResult<PlayPO> fightRes = role.getGame().play.startByRole(role, context, fsResult.data);
      if (!fightRes.ok()) {
        return r.fail(fightRes.message);
      }
      this.fighting = true;
    }

    return r.success();
  }

  /**
   * 英雄阶段或者飞升升星消耗类型任务
   *
   * @param tpl
   * @param sender
   * @param taskRow
   * @param rewardShow
   * @return
   */
  private GameResult consumeMethod(HeroBaseRow tpl, MergeMsgSender sender, HeroStarTaskRow taskRow
          , Map<Integer, CostSelect> csbbMap, RewardShow rewardShow) {
    GameResult r = new GameResult();

    //新升星道具检测
    //1.检测选择道具
    if (!costSelectCheck(csbbMap, taskRow.cost)) {
      role.getGame().notice.message(role, "升星材料选择错误");
      return r.fail("升星材料选择错误");
    }
    CostSelect csWithoutPileProp = new CostSelect();
    csWithoutPileProp.options = new LinkedList<>();
    CostSelect cs = new CostSelect();
    cs.options = new LinkedList<>();
    for (CostSelect singleCs : csbbMap.values()) {
      for (cate.game.res.Option option : singleCs.options) {
        if (option.type == GDObj.Type.HERO) {
          for (String uid : option.uidList) {
            GameResult<Hero> hero_r = role.getGame().train.hero.findHeroResult(role, uid);
            if (hero_r.ok())
              if (hero_r.data.lock.isCustomedLock()) {
                return r.fail("存在上锁英雄");
              }
          }}
        if (option.type != GDObj.Type.PROP
                || role.getGame().table.item.base.get(option.tid).pileMax < 2) {
          if (!csContainsOtherOption(csWithoutPileProp.options, option)) {
            csWithoutPileProp.options.add(option);
          }
        } else {
          cs.options.add(option);
        }
      }
    }
    cs.options.addAll(csWithoutPileProp.options);
    MixRes allCost = new MixRes(taskRow.costProp);
    //特殊道具
    for (Option option : cs.options) {
      allCost.addItem(new MixResItem(option.type, option.tid, option.uidList.size()));
    }

    // 具体的消耗
    EcResult<MixResItem> rCost = allCost.consumeCheck(role, csWithoutPileProp);
    if (!rCost.ok()) {
      role.getGame().notice.tipResItem(role, rCost.data);
      return r.fail(rCost.message);
    }

    // 一定要返回在先，否则会因为英雄被消耗而找不到
    EcResult<RewardShow> rReturn = role.getGame().train.hero.forHeroList(role, cs, starUpOpt());
    if (!rReturn.ok()) {
      return r.fail(rReturn.message);
    }
    if (rReturn.data != null && !rReturn.data.isEmpty()) {
      rewardShow.merge(rReturn.data);
    }
    allCost.consumeDirect(role, cs, sender);
    sender.send(role, true);
    return r.success();
  }

  public static boolean csContainsOtherOption(List<cate.game.res.Option> options, cate.game.res.Option otherOption) {
    for (cate.game.res.Option option : options) {
      if (option.type == otherOption.type && option.tid == otherOption.tid) {
        option.uidList.addAll(otherOption.uidList);
        return true;
      }
    }
    return false;
  }


  public boolean costSelectCheck(Map<Integer, CostSelect> csbbMap, String costStr) {
    Map<Integer, String> singleCostStrMap = new HashMap<>();
    if (StringUtils.isNotBlank(costStr)) {
      int index = 0;
      for (String s : costStr.split("#")) {
        if (StringUtils.isBlank(s)) {
          continue;
        }
        singleCostStrMap.putIfAbsent(index, s);
        index++;
      }
    }

    boolean isMatch = true;
    for (Map.Entry<Integer, String> item : singleCostStrMap.entrySet()) {
      if (csbbMap == null
              || !csbbMap.containsKey(item.getKey())
              || CollectionUtils.isEmpty(csbbMap.get(item.getKey()).options)) {
        return false;
      }
      //match
      CostSelect clientSingleCs = csbbMap.get(item.getKey());

      //二级，分割后: 类型:tids:数量
      ArrayList<String> needOptionList = new ArrayList<>();
      for (String s : item.getValue().split(":")) {
        needOptionList.add(s);
      }
      int objType = Integer.parseInt(needOptionList.get(0));
      String itemStr = needOptionList.get(1);
      int totalNum = Integer.parseInt(needOptionList.get(2));
      int existNum = 0;

      if (objType == GDObj.Type.ITEM_LIST_ALL_AVAILABLE) {
        List<String> strList = new LinkedList<>();

        for (String s : itemStr.split("\\|")) {
          strList.add(s);
        }

        for (Option option : clientSingleCs.options) {
          String optionStr = String.valueOf(option.type) + '$' + String.valueOf(option.tid);
          if (strList.stream().anyMatch(optionStr::equals)) {
            existNum += option.uidList.size();
          }
        }
        isMatch &= (existNum == totalNum);
      } else if (objType >= GDObj.Type.HERO_OF_STAR && objType <= GDObj.Type.HERO_OF_NIUBI_SOUL) {
        for (Option singleOption : clientSingleCs.options) {
          existNum += checkCaclDogCost(singleOption, itemStr, objType);
        }
        isMatch &= (existNum == totalNum);
      } else {
        Option clientSingleOption = clientSingleCs.options.get(0);
        isMatch &= (clientSingleOption.type == objType)
                & (clientSingleOption.tid == Integer.parseInt(itemStr))
                & (clientSingleOption.uidList.size() == totalNum);
      }
    }

    return isMatch;
  }

  private int checkCaclDogCost(Option clientSingleOption, String itemStr, int objType) {
    int needTid = Integer.parseInt(itemStr);
    StarUpItemRow row = role.getGame().table.item.starUp.get(clientSingleOption.tid);

    if (clientSingleOption.type == GDObj.Type.HERO) {
      //实体英雄检测
      HeroBaseRow heroRow = role.getGame().table.hero.base.get(clientSingleOption.tid);
      if (heroRow == null) {
        return 0;
      }
      if (objType == GDObj.Type.HERO_OF_STAR_CAMP) {
        int starReq = needTid / 10;
        int campReq = needTid % 10;
        if (starReq == heroRow.star && (heroRow.camp == ((byte) campReq))) {
          return clientSingleOption.uidList.size();
        }
      } else if (objType == GDObj.Type.HERO_OF_STAR) {
        if (needTid == heroRow.star) {
          return clientSingleOption.uidList.size();
        }
      } else if ((objType == GDObj.Type.HERO_OF_STAR_NIUBI_CAMP ||
              objType == GDObj.Type.HERO_OF_NIUBI_SOUL)) {
        if (needTid == heroRow.star &&
                (GDCamp.Type.isNiuBi(heroRow.camp))) {
          return clientSingleOption.uidList.size();
        }
      } else if (objType == GDObj.Type.HERO_OF_NATURE_CAMP) {
        if (needTid == heroRow.star &&
                (GDCamp.Type.isNature(heroRow.camp))) {
          return clientSingleOption.uidList.size();
        }
      }
      return 0;
    }

    if (objType == GDObj.Type.HERO_OF_STAR_CAMP) {
      int starReq = needTid / 10;
      int campReq = needTid % 10;
      if (starReq == row.star && (row.replaceCampValid((byte) campReq))) {
        return clientSingleOption.uidList.size();
      }
    } else if (objType == GDObj.Type.HERO_OF_STAR) {
      if (needTid == row.star) {
        return clientSingleOption.uidList.size();
      }
    } else if ((objType == GDObj.Type.HERO_OF_STAR_NIUBI_CAMP ||
            objType == GDObj.Type.HERO_OF_NIUBI_SOUL)) {
      if (needTid == row.star &&
              (row.camps.stream().anyMatch(GDCamp.Type::isNiuBi) || row.camps.contains(GDCamp.Type.NULL))) {
        return clientSingleOption.uidList.size();
      }
    } else if (objType == GDObj.Type.HERO_OF_NATURE_CAMP) {
      if (needTid == row.star &&
              (row.camps.stream().anyMatch(GDCamp.Type::isNature) || row.camps.contains(GDCamp.Type.NULL))) {
        return clientSingleOption.uidList.size();
      }
    }
    return 0;
  }

  /**
   * 英雄升星任务完成后
   *
   * @param tpl
   * @param hero
   * @param taskId
   * @return
   */
  public GameResult successStarUpTask(HeroBaseRow tpl, Hero hero, int taskId, RewardShow rewardShow) {
    GameResult r = new GameResult();
    int oldTid = hero.tid;
    Hero compareOldHero = hero.copy();
    hero.starTaskList.add(taskId);

    if (new HashSet<>(hero.starTaskList).size() >= tpl.taskMap.size()) {
      hero.starTaskList.clear();
      hero.tid = tpl.nextStarId;

      if (hero.lock.heroReplace) {
        role.getRes().callPO.nature.release(hero.uid);
        hero.lock.releaseReplaceLock(role);
      }
      HeroBaseRow newHeroRow = role.getGame().table.hero.base.get(hero.tid);
      publishEvent(role, new HeroStarChangeEvent(role, hero.uid, newHeroRow.star, tpl.star, tpl.camp));
      // 历史记录
      role.getHistory().action.heroStarUp(hero, newHeroRow.star);
      role.getHistory().action.heroOccupationCollection(role, hero.getTpl(role.getGame()).job, newHeroRow.star);
      role.getHistory().action.restrictHeroStarUpTo(hero.tid, 1);
    }

    //先触发事件
    publishEvent(role, new HeroPowerChangeEvent(role, hero));
    List<StarUpResVO> res = new ArrayList<>();
    StarUpResVO resVo = new StarUpResVO();
    resVo.uid = hero.uid;
    resVo.tidOld = oldTid;
    resVo.tidNew = hero.tid;
    resVo.taskId = taskId;
    resVo.hero = compareOldHero;

    res.add(resVo);
    HeroBaseUpdateResp.send(role, hero);

    if (hero.lock.heroReplace) {
      role.getRes().callPO.release(null);
      hero.lock.releaseReplaceLock(role);
    }

    if (!res.isEmpty()) {
      role.getBase().updatePower();
      role.sendNow(new HeroBatchStarUpResp(res, rewardShow));
    }

    role.getBag().hero.markHeroChanged();
    return r.success();
  }

  public Predicate<FuncPosition> allowFuncPosition() {
    return funcPosition ->
            funcPosition.getPosition(1).grids.size() <= 2;
  }
}
